<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 函数节流 -->
    <button id="btn">click me</button>
    <hr />

    <!-- 数据驱动视图微框架 -->
    <div>
      <p id="pMyname">{name}</p>
      <p id="pMyage">{age}</p>
      <p id="pGender">{gender}</p>
      <h3 id="hGender">{gender}</h3>
    </div>
    <hr />

    <!-- 总分：100分 -->
    <script>
      /* 请罗列5组请求头+响应头，并简述其含义 */
      function fn1() {
        console.log(1, "Accept", "客户端想要的数据类型");
        console.log(1, "Content-Type", "服务端返回的数据类型");

        console.log(1, "Accept-Encoding", "客户端想要的数据压缩格式");
        console.log(1, "Content-Encoding", "服务端返回的数据压缩格式");

        console.log(2, "If-None-Match", "客户端携带上次数据的指纹");
        console.log(2, "E-Tag", "服务端返回的数据的指纹");

        console.log(3, "If-Modified-Since", "客户端携带上次数据的尾更时间");
        console.log(3, "Last-Modified", "服务端返回的数据的最后更新时间");

        console.log(4, "Origin", "客户端携带自己的域名/IP");
        console.log(4, "Access-Control-Allow-Origin", "服务端允许谁跨域访问");

        console.log(5, "Cookie", "客户端携带Cookie");
        console.log(5, "Set-Cookie", "服务端设置Cookie");

        console.log(5, "User-Agent", "客户端浏览器信息");
        console.log(5, "Server", "服务端信息");
      }
      // fn1();

      /* 使用批处理函数实现如下功能 */
      function fn2() {
        const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

        // 遍历数组
        arr.forEach((item) => console.log(item));

        // 将所有元素映射为对应的平方数，打印映射出的新数组
        console.log(arr.map((item) => Math.pow(item, 2)));

        // 过滤出3的倍数，打印过滤后的结果数组
        console.log(arr.filter((item) => item % 3 === 0));

        // 判断arr中是否所有元素都是3的倍数
        console.log(arr.every((item) => item % 3 === 0));

        // 判断arr中是否有元素都是3的倍数
        console.log(arr.some((item) => item % 3 === 0));

        // 从数组中找出第一个3的倍数
        console.log(arr.find((item) => item % 3 === 0));

        // 求arr中所有元素的和，使用reduce实现
        console.log(arr.reduce((pv, cv) => pv + cv));

        // 在100的基础上，加入arr中所有元素的和，使用reduce实现
        console.log(arr.reduce((pv, cv) => pv + cv, 100));
      }
      // fn2()

      /* 冒泡排序数组 */
      function fn3() {
        function bubbleSort(arr) {
          //实现之
          let temp;
          for (let j = arr.length - 2; j >= 0; j--) {
            for (let i = 0; i <= j; i++) {
              if (arr[i] < arr[i + 1]) {
                temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
              }
            }
          }
          return arr;
        }

        const arr = [3, 1, 4, 5, 9, 2, 6, 8, 7, 0];
        console.log(bubbleSort(arr)); //输出arr的降序排列
      }
      // fn3();

      /* 手撸深拷贝 */
      function fn4() {
        function deepCopy(obj) {
          if (typeof obj !== "object" && typeof obj !== "function") {
            return obj;
          }

          if (typeof obj === "function") {
            return obj;
          }

          if (Array.isArray(obj)) {
            const retArr = [];
            obj.forEach((item) => retArr.push(deepCopy(item)));
            return retArr;
          }

          if (typeof obj === "object") {
            const retObj = {};
            for (let key in obj) {
              retObj[key] = deepCopy(obj[key]);
            }
            return retObj;
          }

          return "其它数据类型";
        }

        /* 不要改动测试代码 */
        function test() {
          // 待拷贝对象
          const obj1 = {
            name: "黑哥",
            age: 18,
            friends: [
              { name: "白鸽", age: 19 },
              {
                name: "宏哥",
                age: 20,
                say() {
                  console.log("hello");
                },
              },
            ],
          };

          /* 得到obj1的深拷贝副本 */
          const obj2 = deepCopy(obj1);

          /* 修改副本 */
          obj2.name = "black";
          obj2.friends[0].age = 29;
          obj2.friends[1].say = () => console.log("hi!");

          /* 观察本体是否受到影响 */
          console.log(obj1.name, obj2.name); //黑哥 black
          console.log(obj1.friends[0].age, obj2.friends[0].age); //19 29
          obj1.friends[1].say(); //hello
          obj2.friends[1].say(); //hi
        }
        test();
      }
      // fn4();

      /* 手撸函数节流 */
      function fn5() {
        /* 自定义节流高阶函数 */
        const myThrottle = (fn, delay = 1000) => {
          let timerForbid = null;
          return function (...args) {
            if (!timerForbid) {
              let ret = fn.apply(null, args);
              timerForbid = setTimeout(() => {
                timerForbid = null;
              }, delay);
              return ret;
            }
          };
        };

        /* 点击事件监听器 */
        const clickHandler = (e) => {
          console.log("clicked", Date.now());
        };

        /* 实现按钮每2秒只响应一次点击事件 */
        btn.addEventListener("click", myThrottle(clickHandler, 2000));
      }
      // fn5();

      /* Promise链式回调 */
      function fn6() {
        /* 云端乘法函数，请不要修改其代码 */
        function multiply(a, b, callback) {
          setTimeout(() => callback(a * b), 500);
        }

        function mulPromise(a, b) {
          return new Promise((resolve, reject) =>
            multiply.apply(null, [a, b, (ret) => resolve(ret)])
          );
        }

        // 基于multiply函数，使用Promise链式回调求得5的阶乘；
        mulPromise(2, 3)
          .then((ret) => mulPromise(ret, 4))
          .then((ret) => mulPromise(ret, 5))
          .then((ret) => console.log("最终结果", ret));
      }
      // fn6();

      /* async-await */
      function fn7() {
        /* 云端乘法函数，请不要修改其代码 */
        function multiply(a, b, callback) {
          setTimeout(() => {
            // 注意这里有机会出错！！！！！
            Math.random() > 0.5 ? callback(a * b) : callback(-1);
          }, 500);
        }

        // 基于multiply函数，使用async-await求得5的阶乘，如果云端出错妥善处理之
        function mulPromise(a, b) {
          return new Promise((resolve, reject) => {
            multiply.apply(null, [
              a,
              b,
              (ret) => {
                ret === -1 ? reject("云端计算失败") : resolve(ret);
              },
            ]);
          });
        }

        async function demo() {
          try {
            let ret = await mulPromise(2, 3);
            ret = await mulPromise(ret, 4);
            ret = await mulPromise(ret, 5);
            console.log(ret);
          } catch (err) {
            console.log("err=", err);
          }
        }
        demo();
      }
      // fn7();

      /* 带有超时功能的Promise */
      function fn8() {
        async function execWithTimeout(fn, ms) {
          return new Promise((resolve, reject) => {
            let timer = setTimeout(async () => {
              reject("timeout");
            }, ms);

            /* 链式回调版 */
            fn.call().then((ret) => {
              clearTimeout(timer);
              resolve(ret);
            });

            /* await版 */
            // let ret = await fn.call()
            // clearTimeout(timer)
            // resolve(ret)
          });
        }

        /* 测试代码 */
        async function fn1() {
          return rq("https://www.taobao.com");
        }

        function rq(url) {
          return new Promise((resolve) =>
            setTimeout(() => resolve(`${url}的页面`), 200)
          );
        }

        execWithTimeout(fn1, 300)
          .then((data) => console.log("data=", data))
          .catch((err) => console.log("err=", err));
      }
      // fn8()

      /* 一次性获取多个指定页面 */
      function fn9() {
        async function getContentByUrl(url) {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve(`${url}的页面内容`);
            }, 1000);
          });
        }

        async function fetchData(urls) {
          // return Promise.allSettled(
          //   urls
          //     .filter((url) => url.startsWith("https://www."))
          //     .map((url) => getContentByUrl(url))
          // );

          const ps = urls
            .filter((url) => url.startsWith("https://www."))
            .map((url) => getContentByUrl(url));
          return Promise.all(ps);
        }

        const urls = [
          "https://www.taobao.com",
          "https://www.baidu.com",
          "https://web.taobao.com",
        ];

        // ["https://www.taobao.com的页面内容","https://www.baidu.com 的页面内容"]
        fetchData(urls).then((result) => console.log(result));
      }
      // fn9();

      /* 手撸一个数据驱动视图微框架 */
      function fn10() {
        function defineReactive(data, keys = []) {
          keys.forEach(({ name, watchers }) => {
            Object.defineProperty(data, name, {
              set(newValue) {
                data[`x_${name}`] = newValue;
                watchers.forEach((w) => (w.textContent = newValue));
              },
              get() {
                return data[`x_${name}`];
              },
            });
          });
        }

        const data = {};

        /* 提前定义响应式数据 */
        defineReactive(data, [
          { name: "age", watchers: [pMyage] },
          { name: "name", watchers: [pMyname] },
          { name: "gender", watchers: [hGender, pGender] },
        ]);

        /* 初始化响应式数据 */
        data.name = "黑哥";
        data.age = 18;
        data.gender = "男";

        /* 响应式数据动态变化 */
        setInterval(() => {
          data.name = Date.now();
          data.age = data.age + 1;
          data.gender = data.gender === "男" ? "女" : "男";
        }, 1000);
      }
      // fn10();

    </script>
  </body>
</html>
